#pragma once
#include <iostream>
#include "model.hpp"
#include <string>
#include <vector>
#include <algorithm>
#include <mutex>
#include <cassert>
#include <fstream>
#include <jsoncpp/json/json.h>
#include "../Comm/Log.hpp"
#include "../Comm/Util.hpp"
#include "../Comm/httplib.h"
#include "view.hpp"
using namespace ns_model;
using namespace ns_log;
using namespace ns_util;
using namespace ns_view;
using namespace httplib;
namespace ns_control
{

    // 这是我们编译的主机
    class Machine
    {
    public:
        std::string ip;  // 主机ip
        int port;        // 主机端口
        uint64_t load;   // 编译服务的负载
        std::mutex *mtx; // c++的mutex不能拷贝，所以使用指针
    public:
        Machine() : ip(""), port(0), load(0), mtx(nullptr)
        {
        }
        ~Machine()
        {
        }

    public:
        void IncLoad()
        {
            if (mtx)
                mtx->lock();
            ++load;
            if (mtx)
                mtx->unlock();
        }
        void Eraseload()
        {
            if(mtx) mtx->lock();
            load=0;
            if(mtx) mtx->unlock();
        }
        void DecLoad()
        {
            if (mtx)
                mtx->lock();
            --load;
            if (mtx)
                mtx->unlock();
        }
        uint64_t Load()
        {
            uint64_t _load;
            if (mtx)
                mtx->lock();
            _load = load;
            if (mtx)
                mtx->unlock();
            return _load;
        }
    };
    // 这是负载均衡模块
    const std::string machine_conf = "./machineconf/service_machine.conf";
    class LoadBlance
    {
    private:
        // 编译服务的所以主机
        // 用下标来表示是那台主机
        vector<Machine> machine;
        // 在线的主机
        vector<int> online;
        // 离线的主机
        vector<int> offline;

        std::mutex mtx;

    public:
        LoadBlance()
        {
            assert(LoadMachine(machine_conf));
        }
        ~LoadBlance()
        {
        }

    public:
        // 加载服务主机
        bool LoadMachine(const std::string &machine_conf)
        {
            std::ifstream in(machine_conf);
            // if(in.is_open())
            // {
            //     LOG(FATAL)<<" 加载 "<<machine_conf<<" 失败 "<<"\n";
            //     return false;
            // }
            in.is_open();
            std::string line;
            std::vector<std::string> tokens;
            while (std::getline(in, line))
            {
                SplitUtil::StringSplit(line, &tokens, ":");
                if (tokens.size() != 2)
                {
                    LOG(WARNing) << " 分割 " << line << " 失败 " << "\n";
                    continue;
                }
                Machine m;
                m.ip = tokens[0];
                m.port = std::atoi(tokens[1].c_str());
                m.load = 0;
                m.mtx = new std::mutex();
                online.push_back(machine.size());
                machine.push_back(m);
            }
            in.close();
            return true;
        }
        // 智能选择负载最低的主机
        // id是输出型参数
        // m是输出型参数
        bool SmartChoice(int *id, Machine **m)
        {
            // 一、选择好主机(更新负载)
            //~选择算法
            // 1、随机数+hash
            // 2、轮询+hash
            // 二、我们需要离线该主机
            // std::cout << "枷锁" << std::endl;
            mtx.lock();
            // std::cout << "以加锁" << std::endl;
            int online_num = online.size();
            if (online_num == 0)
            {
                mtx.unlock();
                LOG(FATAL) << "所以主机都离线了，请运维的同事赶紧检查!" << "\n";
                return false;
            }
            *id = online[0];
            uint64_t min_load = machine[online[0]].Load();
            *m = &machine[online[0]];
            // 循环遍历，找到最低负载的主机
            // std::cout << "开始循环寻找负载最低的" << std::endl;
            for (int i = 1; i < online_num; i++)
            {
                // std::cout << "循环中：" << i << std::endl;
                if (machine[online[i]].Load() < min_load)
                {
                    min_load = machine[online[i]].Load();
                    (*id) = online[i];
                    (*m) = &machine[online[i]];
                }
            }
            // 更新负载
            //(*m)->IncLoad();
            mtx.unlock();
            // std::cout << "解锁" << std::endl;

            return true;
        }
        // 在线主机
        void OnlineMachine()
        {
            mtx.lock();
            online.insert(online.begin(),offline.begin(),offline.end());
            offline.erase(offline.begin(),offline.end());
            mtx.unlock();
            std::cout<<"当前所有主机已经上线...."<<std::endl;
        }
        // 离线主机
        void OffMachine(int which)
        {
            mtx.lock();
            auto iter = online.begin();
            for (; iter != online.end(); iter++)
            {
                if (which == *iter)
                {
                    // 找到挂掉的主机了
                    machine[which].Eraseload();
                    online.erase(iter);
                    offline.push_back(which);
                    break; // 因为有break,所以不考虑迭代器实现的问题
                }
            }
            mtx.unlock();
        }
        // for test
        void MachineShow()
        {
            std::cout << "在线主机列表...";
            for (auto &e : online)
            {
                std::cout << e << " ";
            }
            std::cout << std::endl;
            std::cout << "离线主机列表...";
            for (auto &e : offline)
            {
                std::cout << e << " ";
            }
            std::cout << std::endl;
        }
    };
    // 这是我们核心业务逻辑的控制器
    class Control
    {
    private:
        Model model_;           // 提供后台数据
        View view_;             // 提供html渲染功能
        LoadBlance loadblance_; // 核心负载均衡器
    public:
        Control()
        {
        }
        ~Control()
        {
        }

    public:
        void OnlineMachine()
        {
            loadblance_.OnlineMachine();
        }
        bool GetQuestionAll(std::string *html) // 获取所有题目，并将其转换成网页
        {
            bool ret = true;
            std::vector<struct Questions> tokens;

            if (model_.QuestionAll(&tokens))
            {
                std::sort(tokens.begin(), tokens.end(), [](struct Questions &q1, struct Questions &q2)
                          { return std::atoi(q1.number.c_str()) < std::atoi(q2.number.c_str()); });
                // 网页渲染
                view_.AllExpendHtml(tokens, html);
            }
            else
            {
                *html = "渲染题目失败";
                ret = false;
            }
            return ret;
        }
        bool GetQuestionOne(std::string number, std::string *html)
        {
            struct Questions q;
            bool ret = true;
            if (model_.QuestionOne(number, &q))
            {
                //
                view_.OneExpendHtml(q, html);
            }
            else
            {
                *html = "单道题目渲染失败,编号为:" + number;
                ret = false;
            }
            return ret;
        }
        bool Judge(const std::string &number, const std::string in_json, std::string *out_json)
        {
            //"code":""
            //"input":""
            //
            // 根据题目编号拿到题目细节
            struct Questions q;
            model_.QuestionOne(number, &q);
            // 进行反序列化
            // 1、反序列化，拿到用户提交的input,和代码code
            Json::Reader reader;
            Json::Value value;
            reader.parse(in_json, value);
            std::string code = value["code"].asString();

            // 2、将用户提交的代码和tail合并成一个文件
            Json::Value compile_value;

            compile_value["input"] = value["input"].asString();
            compile_value["code"] = code + "\n" + q.tail;
            compile_value["cpu_limit"] = q.cup_limit;
            compile_value["mem_limit"] = q.mem_limit;
            Json::FastWriter write;
            std::string compile_string = write.write(compile_value);
            // 3、负载均衡的选择主机(规则：一直请求直到请求成功，否则全部挂掉)
            std::cout << "开始选择主机" << std::endl;
            while (true)
            {
                int id = 0;
                Machine *m = nullptr;
                if (!loadblance_.SmartChoice(&id, &m))
                {
                    break;
                }
                
                Client cli(m->ip, m->port);
                m->IncLoad();
                LOG(INFO) << "选择主机成功,主机ID:" << id << "主机详情IP:" << m->ip << "Port:" << m->port <<"当前主机负载为："<<m->Load()<< "\n";
                // 4、先compiler_and_run发送http请求，返回结果
                if (auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
                {
                    // 5、将结果返回给用户
                    if (res->status == 200)
                    {
                        LOG(INFO) << "代码编译并运行成功..." << "\n";
                        *out_json = res->body;
                        m->DecLoad();
                        break;
                    }
                    m->DecLoad();
                }
                else
                {
                    LOG(INFO) << "选择当前主机失败,主机ID:" << id << "主机详情IP:" << m->ip << "Port:" << m->port << "\n";
                    m->DecLoad();
                    loadblance_.OffMachine(id);

                    loadblance_.MachineShow(); // 方便测试
                }
            }
        }
    };
}